package webserver;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import webserver.iterfaces.IHttpResponse;

public class HttpServer {

    private ExecutorService executor = Executors.newCachedThreadPool();
    private PageDispatcher pageDispatcher = new PageDispatcher();

    public static void main(String[] args) throws Throwable {
	new HttpServer().start();
    }

    private void start() throws Throwable {
	ServerSocket ss = new ServerSocket(8083);
	while (true) {
	    Socket s = ss.accept();
	    System.err.println("Client accepted");
	    executor.submit(new SocketProcessor(s));
	}
    }

    private class SocketProcessor implements Runnable {

	private Socket s;
	private InputStream is;
	private OutputStream os;

	private SocketProcessor(Socket s) throws Throwable {
	    this.s = s;
	    this.is = s.getInputStream();
	    this.os = s.getOutputStream();
	}

	public void run() {
	    HttpRequest readInputHeaders = null;
	    try {
		readInputHeaders = readInputHeaders();
		System.err.println(readInputHeaders);
		writeResponse(pageDispatcher.dispatch(readInputHeaders));
	    } catch (Exception t) {
		try {
		    t.printStackTrace();
		    sendError(os, 404, t.getMessage().getBytes());
		} catch (IOException e) {
		    e.printStackTrace();
		}
	    } finally {
		try {
		    s.close();
		} catch (Throwable t) {
		    t.printStackTrace();
		}
	    }
	    System.err.println("Client processing finished");
	}

	private void sendError(OutputStream out, int code, byte[] message)
		throws IOException {
	    sendHeader(out, code, "text/html", message.length);
	    out.write(message);
	}

	private void sendHeader(OutputStream out, int code, String contentType,
		long contentLength) throws IOException {
	    out.write(("HTTP/1.0 "
		    + code
		    + " OK\r\n"
		    + "Date: "
		    + new Date().toString()
		    + "\r\n"
		    + "Server: JibbleWebServer/1.0\r\n"
		    + "Content-Type: "
		    + contentType
		    + "\r\n"
		    + ((contentLength != -1) ? "Content-Length: "
			    + contentLength + "\r\n" : "") + "Last-modified: "
		    + new Date() + "\r\n" + "\r\n").getBytes());
	}

	private void writeResponse(IHttpResponse r) throws Exception {
	    try {
		if (r.getCode() == 200) {
		    byte[] bytes = r.getResponse();
		    sendHeader(os, r.getCode(), r.getContentType(),
			    bytes.length);
		    os.write(bytes);
		} else {
		    sendError(os, r.getCode(), r.getResponse());
		}
	    } finally {
		os.flush();
		os.close();
	    }
	}

	private HttpRequest readInputHeaders() throws Exception {
	    BufferedReader br = new BufferedReader(new InputStreamReader(is));
	    HttpRequest rq = new HttpRequest();
	    String s;
	    while ((s = br.readLine()) != null && s.trim().length() != 0) {
		rq.add(s);
	    }
	    rq.parse();
	    return rq;
	}
    }
}
